<?php
/**********************
**intellistrip_client.inc
**Date Created: 4/13/2009
**Copyright 2009 Tomorrow Tech Industries
**Contact: RPITomorrowTech@gmail.com
**********************/

require_once("shared/xml_rpc/xmlrpc.inc");
require_once("shared/intellistrip.inc");
require_once("shared/errors.inc");

class IntellistripClient extends IntellistripActions 
{
	/*********
	Private members
	********/

	//IP address, DNS name, etc
	private $serverAddress;
	//Port to access on server
	private $serverPort;
	//Path to XML-RPC server on the server
	private $serverPath;

	/*********
	Initialization
	********/

	//Map of connection info
	public function IntellistripClient($args)
	{
		$this->serverAddress = 'localhost';
		if( isset($args['serverAddress']) )
		{
			$this->serverAddress = $args['serverAddress'];
		}

		$this->serverPath = '/software_intellistrip/xml_rpc_server_hardware.php';
		if( isset($args['serverPath']) )
		{
			$this->serverPath = $args['serverPath'];
		}

		$this->serverPort = 80;
		if( isset($args['serverPort']) )
		{
			$this->serverPort = $args['serverPort'];
		}

		//Note that this is NOT the same as the database password
		$this->serverPassword = 'intellimation';
		if( isset($args['serverPassword']) )
		{
			$this->serverPassword = $args['serverPassword'];
		}
		
	}

	/*********
	Accessor/mutator
	********/

	public function setServerAddress($serverAddress)
	{
		$this->serverAddress = $serverAddress;
	}

	public function getServerAddress()
	{
		return $this->serverAddress;
	}

	public function setServerPort($serverPort)
	{
		$this->serverPort = $serverPort;
	}

	public function getServerPort()
	{
		return $this->serverPort;
	}

	public function setServerPath($serverPath)
	{
		$this->serverPath = $serverPath;
	}

	public function getServerPath()
	{
		return $this->serverPath;
	}

	//FIXME: does any code call this function?
	public function getNumberPorts($numPorts)
	{
		return 0;
	}

	/*********
	XML-RPC called functions
	*********/
	
	public function setPortPower($portID, $powerLevel)
	{
		$params = array(new xmlrpcval(array(
				XMLRPC__PASSWORD => new xmlrpcval($this->getServerPassword(), ''),
				XMLRPC__SET_PORT_POWER__PORT_ID => new xmlrpcval($portID, 'int'),
				XMLRPC__SET_PORT_POWER__POWER_LEVEL => new xmlrpcval($powerLevel, 'int')
				),  'struct'));
		
		//Send a message to the server
		$message = new xmlrpcmsg(INTELLISTRIP_SERVER_CLASS . '.' . XMLRPC__SET_PORT_POWER, $params);

		$rc = $this->sendMessage($message, $result);
		if( TT_FAILED($rc) )
		{
			return $rc;
		}

		$struct = $result->value();
		
		if( !$struct->structmemexists(XMLRPC__RC) )
		{
			$this->setLastError("Missing remote return code");
			return TT_GENERAL;
		}
		$rc = $struct->structmem(XMLRPC__RC)->scalarval();
		if( TT_FAILED($rc) )
		{
			$this->setLastError("Remote failure code: " . errorString($rc) . ", error: " . $this->extractError($struct));
			return $rc;
		}
	
		return TT_OK;
	}

	//Get device power
	//
	public function getPortPower($portID, &$powerLevel)
	{
		$params = array(new xmlrpcval(array(
				XMLRPC__PASSWORD => new xmlrpcval($this->getServerPassword(), ''),
				XMLRPC__GET_PORT_POWER__PORT_ID => new xmlrpcval($portID, 'int')
				),  'struct'));
		
		//Send a message to the server
		$message = new xmlrpcmsg(INTELLISTRIP_SERVER_CLASS . '.' . XMLRPC__GET_PORT_POWER, $params);
		
		$rc = $this->sendMessage($message, $result);
		if( TT_FAILED($rc) )
		{
			$this->setLastError("Faild to send message: " . $this->getLastError());
			return $rc;
		}

		$struct = $result->value();
		
		if( !$struct->structmemexists(XMLRPC__RC) )
		{
			$this->setLastError("Missing remote return code");
			return TT_GENERAL;
		}
		$rc = $struct->structmem(XMLRPC__RC)->scalarval();
		if( TT_FAILED($rc) )
		{
			$this->setLastError("Remote failure code: " . errorString($rc) . ", error: " . $this->extractError($struct));
			return $rc;
		}
		$powerLevel = $struct->structmem(XMLRPC__GET_PORT_POWER__POWER_LEVEL)->scalarval();
	
		return TT_OK;
	}

	/********************
	Misc
	********************/
	//Query device information
	//Note this is NOT password protected
	/*******************
	vendorName: vendor supplied name.
		modelNumber: a number unique with respect to a vendor name to identify 
			different products 
		serialNumber: a number unique to an individual device when combined
			with the vendor name and model number
		numberPorts: the number of ports availible on the device
		state: is the power strip on and availible?
	*******************/
	
	public function getIntellistripInformation(&$vendorName, &$modelNumber, 
									&$modelVersion, &$serialNumber,
									&$numberPorts, &$state)
	{
		$params = array(new xmlrpcval(array(), 'struct'));

		// Send a message to the server.
		$message = new xmlrpcmsg(INTELLISTRIP_SERVER_CLASS . '.' . XMLRPC__GET_INTELLISTRIP_INFORMATION,
								 $params);

		$rc = $this->sendMessage($message, $result);
		if( TT_FAILED($rc) )
		{
			return $rc;
		}
		
		$struct = $result->value();

		if( !$struct->structmemexists(XMLRPC__RC) )
		{
			$this->setLastError("Missing remote return code");
			return TT_GENERAL;
		}
		$rc = $struct->structmem(XMLRPC__RC)->scalarval();
		if( TT_FAILED($rc) )
		{
			$this->setLastError("Remote failure code: " . errorString($rc) . ", error: " . $this->extractError($struct));
			return $rc;
		}

		if( !$struct->structmemexists(XMLRPC__GET_INTELLISTRIP_INFORMATION__VENDOR_NAME) )
		{
			$this->setLastError("didn't return key " . XMLRPC__GET_INTELLISTRIP_INFORMATION__VENDOR_NAME);
			return TT_GENERAL;
		}
		$vendorName = $struct->structmem(XMLRPC__GET_INTELLISTRIP_INFORMATION__VENDOR_NAME)->scalarval();

		if( !$struct->structmemexists(XMLRPC__GET_INTELLISTRIP_INFORMATION__MODEL_NUMBER) )
		{
			$this->setLastError("didn't return key " . XMLRPC__GET_INTELLISTRIP_INFORMATION__MODEL_NUMBER);
			return TT_GENERAL;
		}
		$modelNumber = $struct->structmem(XMLRPC__GET_INTELLISTRIP_INFORMATION__MODEL_NUMBER)->scalarval();

		if( !$struct->structmemexists(XMLRPC__GET_INTELLISTRIP_INFORMATION__MODEL_VERSION) )
		{
			$this->setLastError("didn't return key " . XMLRPC__GET_INTELLISTRIP_INFORMATION__MODEL_VERSION);
			return TT_GENERAL;
		}
		$modelVersion = $struct->structmem(XMLRPC__GET_INTELLISTRIP_INFORMATION__MODEL_VERSION)->scalarval();

		if( !$struct->structmemexists(XMLRPC__GET_INTELLISTRIP_INFORMATION__SERIAL_NUMBER) )
		{
			$this->setLastError("didn't return key " . XMLRPC__GET_INTELLISTRIP_INFORMATION__SERIAL_NUMBER);
			return TT_GENERAL;
		}
		$serialNumber = $struct->structmem(XMLRPC__GET_INTELLISTRIP_INFORMATION__SERIAL_NUMBER)->scalarval();

		if( !$struct->structmemexists(XMLRPC__GET_INTELLISTRIP_INFORMATION__NUMBER_PORTS) )
		{
			$this->setLastError("didn't return key " . XMLRPC__GET_INTELLISTRIP_INFORMATION__NUMBER_PORTS);
			return TT_GENERAL;
		}
		$numberPorts = $struct->structmem(XMLRPC__GET_INTELLISTRIP_INFORMATION__NUMBER_PORTS)->scalarval();

		if( !$struct->structmemexists(XMLRPC__GET_INTELLISTRIP_INFORMATION__STATE) )
		{
			$this->setLastError("didn't return key " . XMLRPC__GET_INTELLISTRIP_INFORMATION__STATE);
			return TT_GENERAL;
		}
		$state = $struct->structmem(XMLRPC__GET_INTELLISTRIP_INFORMATION__STATE)->scalarval();
	
		return TT_OK;
	}	
		
	//changePassword
	//newPassword: what Intellistrip's authentication password will be set to if succeeded
	public function changePassword($newPassword)
	{
		return TT_UNSUPPORTED;
		/*
		$params = array(new xmlrpcval(array(
				XMLRPC__PASSWORD => new xmlrpcval($this->getServerPassword(), ''),
				XMLRPC__CHANGE_PASSWORD__NEW_PASSWORD => new xmlrpcval($newPassword, 'int')
				),  'struct'));
		
		//Send a message to the server
		$message = new xmlrpcmsg(INTELLISTRIP_SERVER_CLASS . '.' . XMLRPC__CHANGE_PASSWORD, $params);

		$rc = $this->sendMessage($message, $result);
		if( TT_FAILED($rc) )
		{
			return $rc;
		}
	
		$struct = $result->value();

		if( !$struct->structmemexists(XMLRPC__RC) )
		{
			$this->setLastError("Missing remote return code");
			return TT_GENERAL;
		}
		$rc = $struct->structmem(XMLRPC__RC)->scalarval();
		if( TT_FAILED($rc) )
		{
			$this->setLastError("Remote failure code: " . errorString($rc) . ", error: " . $this->extractError($struct));
			return $rc;
		}

		//Automaticall update the local cache
		$this->setServerPassword($newPassword);
		
		return TT_OK;
		*/
	}
	
	/*********
	Utility functions
	*********/

	protected function extractError($struct)
	{
		if( !$struct->structmemexists(XMLRPC__ERROR) )
		{
			return '*UNSET*';
		}
		$structVal = $struct->structmem(XMLRPC__ERROR);
		if( $structVal )
		{
			$error = $structVal->scalarval();
			if( $error == '' )
			{
				$error = '*BLANK*';
			}
			return $error;
		}
		else
		{
			return '*UNSET: NOT SCALAR*';
		}
	}

	protected function sendMessage($message, &$result)
	{
		if( TT_FAILED($this->getConnection($connection)) )
		{
			if( size($this->getLastError()) == 0 )
			{
				$this->setLastError("Failed DB connect");
			}
			return TT_NETWORK;
		}
				
		$result = $connection->send($message);
		
		//Process the response.
		if( !$result || $result->faultCode() )
		{
			$this->setLastError(
				"Failed send, " . 
				"<BR>" .
				"Fault code: " . $result->faultCode() . 
				"<BR>" .
				"Fault string: " . $result->faultString()
				);
			return TT_GENERAL;
		}

		$struct = $result->value();
		
		$rc = $struct->structmem(XMLRPC__RC)->scalarval();
		if( TT_FAILED($rc) )
		{
			if( $struct->structmemexists(XMLRPC__ERROR) )
			{
				$error = "<not specified>";
			}
			else
			{
				$error = "Failure cause: " . $struct->structmem(XMLRPC__ERROR)->scalarval();
			}

			$this->setLastError("Remote failure code: " . errorString($rc) . ", string: " . $error);
			return $rc;
		}
		return TT_OK;
	}

	protected function getConnection(&$connection)
	{
		$rc = TT_OK;
		// Make an object to represent our server.
		$connection = new xmlrpc_client($this->getServerPath(),
									$this->getServerAddress(), $this->getServerPort())
			or $rc = TT_NETWORK;	
		if( TT_FAILED($rc) )
		{
			$this->setLastError("Couldn't connect for " . $this->getServerPath() . ":" . $this->getServerPort() . "/" . $this->getServerAddress());
		}	
		else
		{
			$connection->setDebug(IntellistripActions::getDebugging());
		}
		return $rc;
	}
}

?>
